package jenkins

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"project-info/src/constants"
	"project-info/src/handle/common"
	"project-info/src/handle/response"
	"project-info/src/lib"
	"project-info/src/model"
	"project-info/src/service"
	"strings"
	"time"

	"github.com/bndr/gojenkins"
	"github.com/cookieY/yee/logger"
	"github.com/xanzy/go-gitlab"
	"golang.org/x/net/websocket"
	"gorm.io/gorm"
)

var parentFolder = []string{"cus-pipeline"}

type Pipeline struct {
	JobName string `json:"job_name"`
	BuildId int64  `json:"build_id"`
}

type JobBuildReq struct {
	ItemVersionId   uint   `json:"item_version_id"`
	Name            string `json:"name"`
	DeployIps       string `json:"deploy_ips"`
	BuildApps       string `json:"build_apps"`
	Branch          string `json:"branch"`
	RollbackVersion string `json:"rollback_version"`
}

type JobRollbackReq struct {
	ItemVersionId     uint `json:"item_version_id"`
	JenkinsJobBuildId uint `json:"jenkins_job_build_id"`
}

type BuildPageReq struct {
	JobName string `json:"job_name"`
	common.PageRequest
}

type RollbackPageReq struct {
	JobName string `json:"job_name"`
	common.PageRequest
}

type TraceJobBuildsReq struct {
	Ids []uint `json:"ids"`
}

type JobBuildRes struct {
	Version             string `json:"version"`
	DeployCluster       string `json:"deploy_cluster"`
	DeployClusterConfig string `json:"deploy_cluster_config"`
	Namespace           string `json:"namespace"`
	DeployConfig        string `json:"deploy_config"`
}

var resourceService = &service.ResourceService{}

// GetBuildBranch 获取job可用构建分支
func GetBuildBranch(c *lib.GinContext) error {
	var itemVersion *model.ItemVersion
	if err := c.Bind(&itemVersion); err != nil {
		c.Logger().Error(err.Error())
		return response.JsonFail(c, "参数解析错误")
	}

	//根据gitUrl获取分支信息
	gitlabClient, err := gitlab.NewClient(model.C.General.GitlabToken, gitlab.WithBaseURL(model.C.General.GitlabApiUrl))
	if err != nil {
		c.Logger().Error("获取gitlab client失败：" + err.Error())
		return response.JsonFail(c, "获取gitlab client失败")
	}
	branchNames := make([]string, 0)
	branches, _, err := gitlabClient.Branches.ListBranches(itemVersion.GitlabId, &gitlab.ListBranchesOptions{})
	for _, branch := range branches {
		branchNames = append(branchNames, branch.Name)
	}

	return response.JsonOk(c, branchNames, "success")
}

// JenkinsJobBuild 构建pipeline
func JenkinsJobBuild(c *lib.GinContext) error {
	var req *JobBuildReq
	if err := c.Bind(&req); err != nil {
		c.Logger().Error("参数错误：" + err.Error())
		return response.JsonFail(c, "参数错误")
	}
	var itemVersion model.ItemVersion
	result := model.DB().Where("id = ?", req.ItemVersionId).First(&itemVersion)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			c.Logger().Error("itemVersion不存在")
			return response.JsonFail(c, "itemVersion不存在")
		}
		c.Logger().Error(result.Error.Error())
		return response.JsonFail(c, "查询pipeline错误")
	}

	var item *model.Item
	result = model.DB().Where("id = ?", itemVersion.ItemId).First(&item)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			c.Logger().Error("项目不存在")
			return response.JsonFail(c, "项目不存在")
		}
		c.Logger().Error(result.Error.Error())
		return response.JsonFail(c, "查询项目错误")
	}

	nameByIds, err := resourceService.GetResourceNameByItem(c, item)
	if err != nil {
		c.Logger().Error(err.Error())
		return response.JsonFail(c, "查询资源名称失败")
	}

	//将build 信息存储到数据库
	jb := &model.JenkinsJobBuild{
		JenkinsJobBuildParams: model.JenkinsJobBuildParams{
			ItemVersionId: itemVersion.Id,
			//Service:        itemVersion.Version,
			Env:               item.Env,
			EnvName:           nameByIds[item.Env],
			App:               item.AppName,
			AppType:           item.AppType,
			GitUrl:            itemVersion.GitUrl,
			GitlabId:          itemVersion.GitlabId,
			GitCredentialId:   itemVersion.GitCredentialId,
			CusDockerfile:     itemVersion.CusDockerfile,
			ExecDir:           itemVersion.ExecDir,
			ToolVersion:       itemVersion.ToolVersion,
			CompileCommand:    itemVersion.CompileCommand,
			RemoteCommand:     item.RemoteCommand,
			DeployDest:        item.DeployDest,
			Archive:           itemVersion.Archive,
			JobName:           concatJenkinsFullJobName(&itemVersion),
			Branch:            req.Branch,
			BuildApps:         req.BuildApps,
			DeployIps:         req.DeployIps,
			DeployType:        item.DeployType,
			DeployCluster:     item.DeployCluster,
			DeployClusterName: nameByIds[item.DeployCluster],
		},
		Time:   time.Now().Format("2006-01-02 15:04:05"),
		Status: "PENDING",
	}

	result = model.DB().Create(jb)
	if result.Error != nil {
		c.Logger().Error(result.Error.Error())
		return response.JsonFail(c, "插入数据错误")
	}

	go asyncJenkinsJobBuild(jb.Id, "deploy", &jb.JenkinsJobBuildParams, req, func(createId uint, build *gojenkins.Build, status string) error {
		if status == constants.EndStatus {
			model.DB().Model(&model.JenkinsJobBuild{}).Where("id = ?", createId).Update("status", build.GetResult())
		} else if status == constants.BuildStatus {
			model.DB().Model(&model.JenkinsJobBuild{}).Where("id = ?", createId).Updates(map[string]interface{}{
				"status":       constants.BuildStatus,
				"build_number": build.GetBuildNumber(),
			})
		} else if status == constants.FailureStatus {
			model.DB().Model(&model.JenkinsJobBuild{}).Where("id = ?", createId).Updates(map[string]interface{}{
				"status": constants.FailureStatus,
			})
		}
		return nil
	})

	return response.JsonOk(c, jb, "构建流水线成功")
}

// JenkinsJobRollback 回滚pipeline
func JenkinsJobRollback(c *lib.GinContext) error {
	var req *JobRollbackReq
	//var req model.JenkinsBuild
	if err := c.Bind(&req); err != nil {
		c.Logger().Error("参数错误：" + err.Error())
		return response.JsonFail(c, "参数错误")
	}

	var jb model.JenkinsJobBuild
	result := model.DB().Where("id = ?", req.JenkinsJobBuildId).First(&jb)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			c.Logger().Error("编译记录不存在")
			return response.JsonFail(c, "编译记录不存在")
		}
		c.Logger().Error(result.Error.Error())
		return response.JsonFail(c, "查询编译记录数据错误")
	}

	var itemVersion model.ItemVersion
	result = model.DB().Where("id = ?", req.ItemVersionId).First(&itemVersion)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			c.Logger().Error("itemVersion不存在")
			return response.JsonFail(c, "itemVersion不存在")
		}
		c.Logger().Error(result.Error.Error())
		return response.JsonFail(c, "查询pipeline错误")
	}

	var item *model.Item
	result = model.DB().Where("id = ?", itemVersion.ItemId).First(&item)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			c.Logger().Error("项目不存在")
			return response.JsonFail(c, "项目不存在")
		}
		c.Logger().Error(result.Error.Error())
		return response.JsonFail(c, "查询项目错误")
	}

	//因为配置比jenkins保存的job_name 多了一个前缀/，所以需要去掉
	var projectInfo model.ProjectInfo
	model.DB().Model(&model.ProjectInfo{}).Where("job_name = ? and build_number = ? and deleted = 0", jb.JobName, jb.BuildNumber).First(&projectInfo)

	nameIds, err := resourceService.GetResourceNameByItem(c, item)
	if err != nil {
		c.Logger().Error(err.Error())
		return response.JsonFail(c, "查询资源名称失败")
	}

	//将build 信息存储到数据库
	jr := &model.JenkinsJobRollback{
		JenkinsJobBuildParams: model.JenkinsJobBuildParams{
			ItemVersionId:     itemVersion.Id,
			Env:               jb.Env,
			EnvName:           nameIds[jb.Env],
			Service:           jb.Service,
			App:               jb.App,
			AppType:           jb.AppType,
			GitUrl:            jb.GitUrl,
			GitlabId:          jb.GitlabId,
			GitCredentialId:   jb.GitCredentialId,
			CusDockerfile:     jb.CusDockerfile,
			ExecDir:           jb.ExecDir,
			ToolVersion:       jb.ToolVersion,
			CompileCommand:    jb.CompileCommand,
			RemoteCommand:     jb.RemoteCommand,
			DeployDest:        jb.DeployDest,
			Archive:           jb.Archive,
			JobName:           jb.JobName,
			Branch:            jb.Branch,
			BuildApps:         jb.BuildApps,
			DeployIps:         jb.DeployIps,
			DeployType:        jb.DeployType,
			DeployCluster:     jb.DeployCluster,
			DeployClusterName: nameIds[jb.DeployCluster],
		},
		Time:                time.Now().Format("2006-01-02 15:04:05"),
		RollbackBuildNumber: jb.BuildNumber,
		Status:              "PENDING",
	}

	result = model.DB().Create(jr)
	if result.Error != nil {
		c.Logger().Error(result.Error.Error())
		return response.JsonFail(c, "插入数据错误")
	}

	//回滚参数
	rollbackVersion := fmt.Sprintf("%d | %s | %s | %s | %s | %s", jb.BuildNumber, projectInfo.Time, projectInfo.App, projectInfo.Branch, projectInfo.Commit, projectInfo.Msg)
	rollbackParams := &JobBuildReq{
		Name:            jb.JobName,
		Branch:          jb.Branch,
		BuildApps:       jb.BuildApps,
		DeployIps:       jb.DeployIps,
		RollbackVersion: rollbackVersion,
	}

	go asyncJenkinsJobBuild(jr.Id, "rollback", &jr.JenkinsJobBuildParams, rollbackParams, func(createId uint, build *gojenkins.Build, status string) error {
		if status == constants.EndStatus {
			model.DB().Model(&model.JenkinsJobRollback{}).Where("id = ?", createId).Update("status", build.GetResult())
		} else if status == constants.BuildStatus {
			model.DB().Model(&model.JenkinsJobRollback{}).Where("id = ?", createId).Updates(map[string]interface{}{
				"status":       constants.BuildStatus,
				"build_number": build.GetBuildNumber(),
			})
		} else if status == constants.FailureStatus {
			model.DB().Model(&model.JenkinsJobRollback{}).Where("id = ?", createId).Update("status", constants.FailureStatus)
		}
		return nil
	})

	return response.JsonOk(c, jr, "构建回滚流水线成功")
}

// GetJenkinsJobBuildLog 获取流水线日志
func GetJenkinsJobBuildLog(c *lib.GinContext) error {
	websocket.Server{
		Handshake: func(config *websocket.Config, request *http.Request) error {
			return nil
		},
		Handler: func(ws *websocket.Conn) {
			ctx := context.Background()
			defer func() {
				c.Logger().Info("关闭连接")
				ws.Close()
			}()
			for {
				var b []byte
				var p *Pipeline
				err := websocket.Message.Receive(ws, &b)
				if err != nil {
					c.Logger().Error("读取消息失败：" + err.Error())
					break
				}
				if string(b) == "ping" {
					continue
				}

				if err := json.Unmarshal(b, &p); err != nil {
					c.Logger().Error("解析消息失败：" + err.Error())
					break
				}

				if p.BuildId == 0 {
					c.Logger().Error("构建id为0，未在jenkins job中执行")
					_ = websocket.Message.Send(ws, "当前记录未在jenkins job中产生构建")
					break
				}

				jenkinsConn := model.JenkinsConnection
				jenkinsJobName := p.JobName
				if strings.HasPrefix(p.JobName, "/") {
					jenkinsJobName = p.JobName[1:]
				}
				jobNamePaths := strings.Split(jenkinsJobName, "/")
				jobNameIndex := len(jobNamePaths) - 1
				job, err := jenkinsConn.GetJob(ctx, jobNamePaths[jobNameIndex], jobNamePaths[:jobNameIndex]...)
				if err != nil {
					c.Logger().Error("获取job信息失败：" + err.Error())
					break
				}
				build, err := job.GetBuild(ctx, p.BuildId)
				//build, err := jenkinsConn.GetBuildFromQueueID(ctx, p.QueueId)
				if err != nil {
					if strings.Contains(err.Error(), "404") {
						_ = websocket.Message.Send(ws, "Jenkins只保留最近5次构建日志")
						break
					}
					c.Logger().Error("获取构建信息失败：" + err.Error())
					break
				}

				position := int64(0)
				hasMore := true
				text := ""
				for build.IsRunning(ctx) || hasMore {
					position, text, hasMore, err = getLogs(ctx, build, position)
					if err != nil {
						c.Logger().Error("获取日志失败：" + err.Error())
					}
					if len(text) > 0 {
						//返回日志到前端
						_ = websocket.Message.Send(ws, text)
					}
				}

				if !hasMore {
					break
				}
			}
		},
	}.ServeHTTP(c.Response(), c.Request())
	return nil
}

// GetJobBuild 获取流水线构建信息
func GetJobBuilds(c *lib.GinContext) error {
	var req *BuildPageReq
	if err := c.Bind(&req); err != nil {
		c.Logger().Error("参数错误：" + err.Error())
		return response.JsonFail(c, "参数错误")
	}

	start, end := lib.Paging(req.Page, req.Size)

	var jenkinsBuilds []model.JenkinsJobBuild
	var total int64
	model.DB().Model(&model.JenkinsJobBuild{}).Where("job_name = ?", req.JobName).Count(&total).Order("id desc").Offset(start).Limit(end).Find(&jenkinsBuilds)
	return response.JsonOk(c, map[string]interface{}{"records": jenkinsBuilds, "total": total}, "获取流水线构建信息成功")
}

// GetRollbacks 获取回滚信息
func GetRollbacks(c *lib.GinContext) error {
	var req *RollbackPageReq
	if err := c.Bind(&req); err != nil {
		c.Logger().Error("参数错误：" + err.Error())
		return response.JsonFail(c, "参数错误")
	}

	start, end := lib.Paging(req.Page, req.Size)

	var rollbacks []model.JenkinsJobRollback
	var total int64
	model.DB().Model(&model.JenkinsJobRollback{}).Where("job_name = ?", req.JobName).Count(&total).Order("id desc").Offset(start).Limit(end).Find(&rollbacks)
	return response.JsonOk(c, map[string]interface{}{"records": rollbacks, "total": total}, "获取回滚信息成功")
}

// WsJobBuildStatus 监听流水线构建状态
func WsJobBuildStatus(c *lib.GinContext) error {
	websocket.Server{
		Handshake: func(config *websocket.Config, request *http.Request) error {
			return nil
		},
		Handler: func(ws *websocket.Conn) {
			exit := make(chan bool)
			msg := make(chan string)
			traceReqChan := make(chan *TraceJobBuildsReq)
			end := make(chan bool)
			defer func() {
				c.Logger().Info("关闭连接")
				close(exit)
				close(msg)
				close(traceReqChan)
				close(end)
				ws.Close()
			}()

			//独立接收消息
			go readMsg(ws, exit, traceReqChan)

			//跟踪jobBuild状态,并发送消息给前端
			traceReq := <-traceReqChan
			go traceJobBuilds(traceReq, ws, exit, end)
			go validateJenkinsBuildTimeOut(traceReq.Ids, "FAILURE")

			select {
			case <-exit:
				c.Logger().Info("前端断开结束")
			case <-end:
				c.Logger().Info("后端执行完毕结束")
			}
		},
	}.ServeHTTP(c.Response(), c.Request())
	return nil
}

// WsJobRollbackStatus 监听回滚构建状态
func WsJobRollbackStatus(c *lib.GinContext) error {
	websocket.Server{
		Handshake: func(config *websocket.Config, request *http.Request) error {
			return nil
		},
		Handler: func(ws *websocket.Conn) {
			exit := make(chan bool)
			msg := make(chan string)
			traceReqChan := make(chan *TraceJobBuildsReq)
			end := make(chan bool)
			defer func() {
				c.Logger().Info("关闭连接")
				close(exit)
				close(msg)
				close(traceReqChan)
				close(end)
				ws.Close()
			}()

			//独立接收消息
			go readMsg(ws, exit, traceReqChan)

			//跟踪jobBuild状态,并发送消息给前端
			traceReq := <-traceReqChan
			go traceJobRollbacks(traceReq, ws, exit, end)
			go validateRollbackTimeOut(traceReq.Ids, "FAILURE")

			select {
			case <-exit:
				c.Logger().Info("前端断开结束")
			case <-end:
				c.Logger().Info("后端执行完毕结束")
			}
		},
	}.ServeHTTP(c.Response(), c.Request())
	return nil
}

// readMsg 读取消息
func readMsg(ws *websocket.Conn, exit chan bool, traceReqChan chan *TraceJobBuildsReq) {
	for {
		var b []byte
		err := websocket.Message.Receive(ws, &b)
		if err != nil {
			if opErr, ok := err.(*net.OpError); ok && opErr.Op == "read" {
				if opErr.Unwrap().Error() == "use of closed network connection" || opErr.Unwrap().Error() == "EOF" {
					logger.DefaultLogger.Info("连接已关闭,退出读取消息")
					break
				}
			}
			logger.DefaultLogger.Error("读取消息失败：" + err.Error())
			break
		}
		s := string(b)
		if s == "ping" {
			websocket.Message.Send(ws, "pong")
			continue
		} else if s == "close" {
			exit <- true
			break
		}

		var traceReq *TraceJobBuildsReq
		if err := json.Unmarshal(b, &traceReq); err != nil {
			logger.DefaultLogger.Error("解析消息失败：" + err.Error())
			break
		}
		traceReqChan <- traceReq
	}
}

// traceJobBuilds 跟踪jobBuild状态
func traceJobBuilds(traceReq *TraceJobBuildsReq, ws *websocket.Conn, exit chan bool, end chan bool) error {
	var jbs []*model.JenkinsJobBuild
	var buildingReturnMap = make(map[uint]bool)
	//如果超过30分钟还没有构建成功，就认为构建失败
	pre30MinTime := time.Now().Add(-30 * time.Minute).Format("2006-01-02 15:04:05")
	traceIds := traceReq.Ids
	for {
		select {
		case <-time.Tick(time.Second * 5):
			result := model.DB().Model(&model.JenkinsJobBuild{}).Where("id in (?) and time > ?", traceIds, pre30MinTime).Find(&jbs)
			if result.Error != nil {
				end <- true
				return result.Error
			}
			if len(jbs) == 0 {
				end <- true
				return nil
			}
			for _, jb := range jbs {
				if jb.Status == "BUILDING" {
					if !buildingReturnMap[jb.Id] {
						if err := sendMsg(ws, jb); err != nil {
							end <- true
							return err
						}
						buildingReturnMap[jb.Id] = true
					}
					continue
				} else if jb.Status == "SUCCESS" || jb.Status == "FAILURE" {
					if err := sendMsg(ws, jb); err != nil {
						end <- true
						return err
					}
					//删除traceIds中已经构建完成的jenkinsBuild
					for i, id := range traceIds {
						if id == jb.Id {
							traceIds = append(traceIds[:i], traceIds[i+1:]...)
							break
						}
					}
					//如果traceIds中没有数据了，就没有必要继续跟踪了,关闭连接
					if len(traceIds) == 0 {
						end <- true
						return nil
					}
				}
			}
		case <-exit:
			return nil
		}
	}
}

// traceJobRollbacks 跟踪jobRollbacks状态
func traceJobRollbacks(traceReq *TraceJobBuildsReq, ws *websocket.Conn, exit chan bool, end chan bool) error {
	var jbs []*model.JenkinsJobRollback
	var buildingReturnMap = make(map[uint]bool)
	//如果超过30分钟还没有构建成功，就认为构建失败
	pre30MinTime := time.Now().Add(-30 * time.Minute).Format("2006-01-02 15:04:05")
	traceIds := traceReq.Ids
	for {
		select {
		case <-time.Tick(time.Second * 5):
			result := model.DB().Model(&model.JenkinsJobRollback{}).Where("id in (?) and time > ?", traceIds, pre30MinTime).Find(&jbs)
			if result.Error != nil {
				end <- true
				return result.Error
			}
			if len(jbs) == 0 {
				end <- true
				return nil
			}
			for _, jb := range jbs {
				if jb.Status == "BUILDING" {
					if !buildingReturnMap[jb.Id] {
						if err := sendRollbackMsg(ws, jb); err != nil {
							end <- true
							return err
						}
						buildingReturnMap[jb.Id] = true
					}
					continue
				} else if jb.Status == "SUCCESS" || jb.Status == "FAILURE" {
					if err := sendRollbackMsg(ws, jb); err != nil {
						end <- true
						return err
					}
					//删除traceIds中已经构建完成的jenkinsBuild
					for i, id := range traceIds {
						if id == jb.Id {
							traceIds = append(traceIds[:i], traceIds[i+1:]...)
							break
						}
					}
					//如果traceIds中没有数据了，就没有必要继续跟踪了,关闭连接
					if len(traceIds) == 0 {
						end <- true
						return nil
					}
				}
			}
		case <-exit:
			return nil
		}
	}
}

// 验证jenkinsBuild是否超时,如果超时就设置为失败
func validateJenkinsBuildTimeOut(ids []uint, timeOutStatus string) error {
	var jbs []*model.JenkinsJobBuild
	result := model.DB().Model(&model.JenkinsJobBuild{}).Where("id in (?)", ids).First(&jbs)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return errors.New("jenkinsBuild不存在")
		} else {
			return result.Error
		}
	}

	layout := "2006-01-02 15:04:05"
	for _, jb := range jbs {
		if jb.Status == "SUCCESS" || jb.Status == "FAILURE" {
			return nil
		}

		jbTime, err := time.Parse(layout, jb.Time)
		if err != nil {
			return err
		}
		if time.Now().After(jbTime.Add(30 * time.Minute)) {
			model.DB().Model(&model.JenkinsJobBuild{}).Where("id = ?", jb.Id).Update("status", timeOutStatus)
		}
	}
	return nil
}

// 验证rollback是否超时,如果超时就设置为失败
func validateRollbackTimeOut(ids []uint, timeOutStatus string) error {
	var jbs []*model.JenkinsJobRollback
	result := model.DB().Model(&model.JenkinsJobRollback{}).Where("id in (?)", ids).First(&jbs)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return errors.New("回滚记录不存在")
		} else {
			return result.Error
		}
	}

	layout := "2006-01-02 15:04:05"
	for _, jb := range jbs {
		if jb.Status == "SUCCESS" || jb.Status == "FAILURE" {
			return nil
		}

		jbTime, err := time.Parse(layout, jb.Time)
		if err != nil {
			return err
		}
		if time.Now().After(jbTime.Add(30 * time.Minute)) {
			model.DB().Model(&model.JenkinsJobRollback{}).Where("id = ?", jb.Id).Update("status", timeOutStatus)
		}
	}
	return nil
}

func sendMsg(ws *websocket.Conn, jb *model.JenkinsJobBuild) error {
	b, _ := json.Marshal(jb)
	if err := websocket.Message.Send(ws, string(b)); err != nil {
		return err
	}
	return nil
}

func sendRollbackMsg(ws *websocket.Conn, jb *model.JenkinsJobRollback) error {
	b, _ := json.Marshal(jb)
	if err := websocket.Message.Send(ws, string(b)); err != nil {
		return err
	}
	return nil
}

func concatJenkinsDeleteJobName(itemVersion *model.ItemVersion) string {
	return strings.Join(parentFolder, "/job") + "/job/" + concatJenkinsJobName(itemVersion)
}

func concatJenkinsFullJobName(itemVersion *model.ItemVersion) string {
	return strings.Join(parentFolder, "/") + "/" + concatJenkinsJobName(itemVersion)
}

func concatJenkinsJobName(itemVersion *model.ItemVersion) string {
	return fmt.Sprintf("%d", itemVersion.Id)
}

func concatJenkinsJobNameByBuildParams(params *model.JenkinsJobBuildParams) string {
	return fmt.Sprintf("%d", params.ItemVersionId)
}

func asyncJenkinsJobBuild(createId uint, mod string, params *model.JenkinsJobBuildParams, req *JobBuildReq, buildFun func(createId uint, build *gojenkins.Build, status string) error) (int64, error) {
	ctx := context.Background()
	jobName := concatJenkinsJobNameByBuildParams(params)
	jobFullName := strings.Join(parentFolder, "/job/") + "/job/" + jobName
	queueId, err := model.JenkinsConnection.BuildJob(ctx, jobFullName, map[string]string{
		"GIT_URL":          params.GitUrl,
		"ENV":              params.Env,
		"SERVICE":          params.Service,
		"APP":              params.App,
		"APP_TYPE":         params.AppType,
		"ARCHIVE":          params.Archive,
		"EXEC_DIR":         params.ExecDir,
		"COMPILE_COMMAND":  params.CompileCommand,
		"DEPLOY_TYPE":      params.DeployType,
		"DEPLOY_CLUSTER":   params.DeployCluster,
		"CUS_DOCKERFILE":   params.CusDockerfile,
		"DEPLOY_DEST":      params.DeployDest,
		"REMOTE_COMMAND":   params.RemoteCommand,
		"TOOL_VERSION":     strings.Join(params.ToolVersion, ","),
		"DEPLOY_IPS":       req.DeployIps,
		"BUILD_APPS":       req.BuildApps,
		"BRANCH":           req.Branch,
		"MOD":              mod,
		"ROLLBACK_VERSION": req.RollbackVersion,
	})
	if err != nil {
		logger.DefaultLogger.Error("触发jenkins编译出错: " + err.Error())
		buildFun(createId, nil, "FAILURE")
		return 0, err
	}

	var buildNum int64
	i := 0
	for {
		task, err := model.JenkinsConnection.GetQueueItem(ctx, queueId)
		if err != nil {
			break
		}

		if task.Raw.Executable.Number != 0 {
			buildNum = task.Raw.Executable.Number
			break
		}

		if task != nil && task.Raw != nil && task.Raw.BuildableStartMilliseconds != 0 {
			time.Sleep(time.Millisecond * time.Duration(task.Raw.BuildableStartMilliseconds))
		} else {
			time.Sleep(time.Second)
		}
		i++
		if i > 60 {
			break
		}
	}

	if buildNum > 0 {
		job, err := model.JenkinsConnection.GetJob(ctx, jobName, parentFolder...)
		if err != nil {
			return buildNum, err
		}
		build, err := job.GetBuild(ctx, buildNum)
		if err != nil {
			return buildNum, err
		}

		//更新状态,build_number
		buildFun(createId, build, constants.BuildStatus)

		for {
			if build.IsRunning(ctx) {
				time.Sleep(time.Second * 10)
			} else {
				//更新状态
				buildFun(createId, build, constants.EndStatus)
				break
			}
		}
	}
	return buildNum, err
}

func getLogs(ctx context.Context, build *gojenkins.Build, position int64) (int64, string, bool, error) {
	res, err := build.GetConsoleOutputFromIndex(ctx, position)
	if err != nil {
		return position, "", false, err
	}
	return res.Offset, res.Content, res.HasMoreText, nil
}

// GetJobBuildInfo jenkins job 获取编译时相关的信息
func GetJobBuildInfo(c *lib.GinContext) error {
	jobName := c.QueryParam("job_name")
	buildNumber := c.QueryParam("build_number")
	mod := c.QueryParam("mod")

	var jbp model.JenkinsJobBuildParams
	if mod == "deploy" {
		var jenkinsJobBuild model.JenkinsJobBuild
		model.DB().Where("job_name = ? and build_number = ?", jobName, buildNumber).First(&jenkinsJobBuild)

		if jenkinsJobBuild.Status != constants.BuildStatus {
			return response.JsonFail(c, "当前jenkins任务未在运行，不能查询相关信息")
		}
		jbp = jenkinsJobBuild.JenkinsJobBuildParams
	} else if mod == "rollback" {
		var jenkinsJobRollback model.JenkinsJobRollback
		model.DB().Where("job_name = ? and build_number = ?", jobName, buildNumber).First(&jenkinsJobRollback)
		if jenkinsJobRollback.Status != constants.BuildStatus {
			return response.JsonFail(c, "当前jenkins任务未在运行，不能查询相关信息")
		}
		jbp = jenkinsJobRollback.JenkinsJobBuildParams
	}

	var itemVersion model.ItemVersion
	model.DB().Where("id = ?", jbp.ItemVersionId).First(&itemVersion)

	var item *model.Item
	model.DB().Where("id = ?", itemVersion.ItemId).First(&item)

	//var k8sCluster model.K8sCluster
	//model.DB().Where("cluster = ?", item.DeployCluster).First(&k8sCluster)
	k8sCluster, err := resourceService.GetK8sConfig(c, item)
	if err != nil {
		c.Logger().Error(err.Error())
		return response.JsonFail(c, "k8s集群配置信息查找失败")
	}

	jobBuildRes := JobBuildRes{
		Version:             itemVersion.Version,
		DeployCluster:       k8sCluster.ClusterName,
		DeployClusterConfig: k8sCluster.Config,
		Namespace:           item.Namespace,
		DeployConfig:        itemVersion.DeployConfig,
	}
	return response.JsonOk(c, jobBuildRes, "ok")
}
